package com.njcb.ams.bootconfig;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.boot.web.server.ConfigurableWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcb.ams.support.listener.ContextLoaderListener;
import com.njcb.ams.support.springmvc.converter.AmsMappingJackson2HttpMessageConverter;
import com.njcb.ams.web.interceptor.ExceptionResolver;
import com.njcb.ams.web.interceptor.GlobalInfoInterceptor;

import freemarker.template.utility.XmlEscape;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;

/**
 * @author LOONG
 */
@Configuration
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {

	/**
	 * 允许所有的跨域访问
	 */
	@Override
	public void addCorsMappings(CorsRegistry registry) {
		registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "HEAD", "POST", "PUT", "PATCH", "DELETE",
				"OPTIONS", "TRACE");
	}

	/**
	 * 添加swagger服务契约资源
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
		registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
		registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
		registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
	}

	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new GlobalInfoInterceptor()).addPathPatterns("/**").excludePathPatterns("doc.html",
				"swagger-ui.html", "/webjars/**");
	}

	/**
	 * ListenerBean注册监听器
	 * @return 监听器
	 */
	@Bean
	public ServletListenerRegistrationBean<ContextLoaderListener> servletListenerRegistrationBean() {
		return new ServletListenerRegistrationBean<ContextLoaderListener>(new ContextLoaderListener());
	}

	@Override
	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
		ExceptionResolver resolver = new ExceptionResolver();
		// 错误页面
		resolver.setDefaultErrorView("/error/error.ftl");
		List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
		messageConverters.add(new ByteArrayHttpMessageConverter());
		// JSON转换器无需设置mediaType,由外部客户端调用时，手动设置相关mediaType
		messageConverters.add(new AmsMappingJackson2HttpMessageConverter());
		resolver.setMessageConverters(messageConverters);
		exceptionResolvers.add(resolver);
	}

	/**
	 * 以编程方式配置嵌入式servlet容器，可以通过注册实现该 WebServerFactoryCustomizer 接口的Spring bean
	 * @return WebServerFactoryCustomizer
	 */
	@Bean
	public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer() {
		return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>() {
			@Override
			public void customize(ConfigurableWebServerFactory factory) {
				ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/html/error/error400.html");
				ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/html/error/error404.html");
				ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/html/error/error500.html");
				factory.addErrorPages(errorPage400, errorPage404, errorPage500);
			}
		};
	}

	@Bean(name = "stringConverter")
	public StringHttpMessageConverter stringConverter() {
		StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
		List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
		MediaType mediaHtmlType = MediaType.parseMediaType("text/html;charset=UTF-8");
		MediaType mediaPlainType = MediaType.parseMediaType("text/plain;charset=UTF-8");
		supportedMediaTypes.add(mediaHtmlType);
		supportedMediaTypes.add(mediaPlainType);
		stringConverter.setSupportedMediaTypes(supportedMediaTypes);
		return stringConverter;
	}

	@Bean(name = "jsonConverter")
	public AmsMappingJackson2HttpMessageConverter jsonConverter() {
		AmsMappingJackson2HttpMessageConverter jacksonConverter = new AmsMappingJackson2HttpMessageConverter();
		List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
		MediaType mediaJsonType = MediaType.parseMediaType("application/json;charset=UTF-8");
		MediaType mediaUrleType = MediaType.parseMediaType("application/x-www-form-urlencoded;charset=UTF-8");
		supportedMediaTypes.add(mediaJsonType);
		supportedMediaTypes.add(mediaUrleType);
		jacksonConverter.setSupportedMediaTypes(supportedMediaTypes);
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
		jacksonConverter.setObjectMapper(objectMapper);
		return jacksonConverter;
	}

	@Bean(name = "viewResolverJson")
	public RequestMappingHandlerAdapter viewResolverJson(StringHttpMessageConverter stringConverter,
			AmsMappingJackson2HttpMessageConverter jsonConverter) {
		RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter();
		List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
		messageConverters.add(stringConverter);
		messageConverters.add(jsonConverter);
		adapter.setMessageConverters(messageConverters);
		return adapter;
	}

	@Bean(name = "viewResolverFtl")
	public FreeMarkerViewResolver viewResolverFtl() {
		FreeMarkerViewResolver adapter = new FreeMarkerViewResolver();
		adapter.setContentType("text/html;charset=utf-8");
		adapter.setViewNames("*.ftl");
		adapter.setCache(false);
		adapter.setOrder(0);
		return adapter;
	}

	@Bean(name = "viewResolverHtml")
	public InternalResourceViewResolver viewResolverHtml() {
		InternalResourceViewResolver adapter = new InternalResourceViewResolver();
		adapter.setPrefix("");
		adapter.setSuffix(".html");
		adapter.setOrder(1);
		return adapter;
	}

	@Bean(name = "fmXmlEscape")
	public XmlEscape fmXmlEscape() {
		return new XmlEscape();
	}

	// freeMarker模板路径
	@Bean(name = "freeMarkerConfig")
	public FreeMarkerConfigurer freeMarkerConfig(XmlEscape fmXmlEscape) {
		FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
		Map<String, Object> variables = new HashMap<String, Object>();
		variables.put("xml_escape", fmXmlEscape);
		configurer.setFreemarkerVariables(variables);

		configurer.setDefaultEncoding("UTF-8");

		Properties settings = new Properties();
		settings.setProperty("template_update_delay", "3600");
		settings.setProperty("locale", "zh_CH");
		settings.setProperty("datetime_format", "yyyy-MM-dd HH:mm:ss");
		settings.setProperty("date_format", "yyyy-MM-dd");
		settings.setProperty("number_format", "#.##");
		configurer.setFreemarkerSettings(settings);

		configurer.setTemplateLoaderPath("classpath:/template/");

		return configurer;
	}

}
